#ifndef _vector_hpp_
#define _vector_hpp_

/** \file vector.hpp Contains the template class vector, that represents a mathematical vector.
 *  \author Adrian Schweizer
 *  \created  $Do 23 Aug 10:32:12 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $Fr 23 Nov 09:12:54 pm CET 2007 schwadri@SchwadriLaptop.local$
 */

#include <cmath>
#include <algorithm>
#include <functional>
#include <numeric>
#include <iterator>
#include <iosfwd>

#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/preprocessor/empty.hpp>
#include <boost/preprocessor/identity.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/facilities/expand.hpp>
#include <boost/preprocessor/comparison/greater_equal.hpp>
#include <boost/preprocessor/comparison/less.hpp>
#include <boost/serialization/access.hpp>
//#include <boost/serialization/nvp.hpp>

#include "norm.hpp"
#include "scalar_product.hpp"
#include "vector_traits.hpp"
#include "transform.hpp"
#include "init.hpp"

#ifndef CORE_MATH_LIMIT_VECTOR_DIMENSION
#define CORE_MATH_LIMIT_VECTOR_DIMENSION   6
#endif


namespace core {

    namespace math {

        //forward decl
        /*template <class T,int D>
            class matrix;
        */

        /** \brief Represents a mathematical vector.*/
        template<class T, int D, class Derived>
            class basic_vector
            {
            public:
                typedef basic_vector<T,D,Derived>   my_type;
                typedef T                           value_type;
                typedef value_type*                 pointer;
                typedef const value_type*           const_pointer;
                typedef pointer                     iterator;
                typedef const_pointer               const_iterator;

                /** \name Structors */      //@{

                ///< Trivial Constructor
                basic_vector()
                { }

                ///< Copy-Constructor
                template<class T2>
                    basic_vector(const basic_vector<T2,D,Derived>& cpyvec)
                    {
                        using std::copy;
                        copy(cpyvec.begin(),cpyvec.end(),m_data);
                    }
                ///< Iterator Structor
                template<class iter>
                    explicit basic_vector(iter front)
                    {
                        using std::copy;
                        copy(front,front+D,m_data);
                    }

                explicit basic_vector(value_type value)
                {
                    using std::uninitialized_fill;
                    uninitialized_fill(m_data,m_data+D,value);
                }

                //@}

                /** \name Iterators */ //@{
                inline iterator  begin()
                {
                    return  m_data;
                }

                inline iterator end()
                {
                    return &m_data[D];
                }

                inline const_iterator   begin() const
                {
                    return  m_data;
                }

                inline const_iterator   end() const
                {
                    return &m_data[D];
                }

                //@}

                /** \name Operators */ //@{
                inline  value_type& operator[](const int i)
                {
                    return m_data[i];
                }

                inline  value_type  operator[](const int i) const
                {
                    return m_data[i];
                }

                /*template<class T2>
                    inline Derived&    operator=(const basic_vector<T2,D,Derived>& v)
                    {
                        using std::copy;
                        copy(v.begin(),v.end(),m_data);

                        return static_cast<Derived&>(*this);
                    }*/

                /*template<class S>
                    set_entry<Derived,1,D> operator=(const S& value)
                    {
                        m_data[0] = value;
                        return set_entry<Derived,1,D>(static_cast<Derived&>(*this));
                    }*/

                template <class S>
                    my_type&  operator *= (const S& skalar)
                    {
                        for(iterator aij = begin();aij!=end();++aij)
                            *aij    *=  skalar;

                        return static_cast<Derived&>(*this);
                    }

                template <class S>
                    Derived&  operator += (const S& skalar)
                    {
                        for(iterator aij = begin();aij!=end();++aij)
                            *aij    +=  skalar;

                        return static_cast<Derived&>(*this);
                    }

                template <class S>
                    Derived&  operator -= (const S& skalar)
                    {
                        for(iterator aij = begin();aij!=end();++aij)
                            *aij    -=  skalar;

                        return static_cast<Derived&>(*this);
                    }

                template <class S>
                    Derived&  operator /= (const S& skalar)
                    {
                        for(iterator aij = begin();aij!=end();++aij)
                            *aij    /=  skalar;

                        return static_cast<Derived&>(*this);
                    }

                //template <class T2>
                    Derived&  operator += (const Derived& vec)
                    {
                        //typename basic_vector<T2,D,Derived>::const_iterator bij = vec.begin();
                        const_iterator bij = vec.begin();

                        for(iterator aij = begin();aij<end();++aij, ++bij)
                            *aij    +=  static_cast<value_type>(*bij);

                        return static_cast<Derived&>(*this);
                    }

                Derived&  operator -= (const Derived& vec)
                {
                    const_iterator bij = vec.begin();

                    for(iterator aij = begin();aij<end();++aij, ++bij)
                        *aij    -=  *bij;

                    return static_cast<Derived&>(*this);
                }

                Derived& operator *= (const Derived& vec)
                {
                    const_iterator bij = vec.begin();

                    for(iterator aij = begin();aij<end();++aij, ++bij)
                        *aij    *=  *bij;

                    return static_cast<Derived&>(*this);
                }

                ///< Normalizes the basic_vector Based on its Euclidian Norm
                ///DEPRECATED:
                Derived&    normalize()
                {
                    value_type  norm=static_cast<value_type>(0);
                    for(const_iterator iter=begin();iter!=end();++iter)
                    {
                            norm  +=  (*iter) * (*iter);
                    }
					
					norm    =   std::sqrt(norm);

                    using std::transform;
                    transform(begin(),end(),begin(),std::bind2nd(std::divides<T>(),norm));
                    return static_cast<Derived&>(*this);
                }

            private:

                //serialization for boost::archive
                friend class boost::serialization::access;
                template<class Archive>
                    void serialize(Archive & ar, const unsigned int version)
                    {
                        //ar & m_data;
                        for(iterator iter=begin();iter!=end();++iter)
                            ar & *iter;
                    }

                //template<class Der,int I, int N>
                friend struct set_entry<Derived,1,D>;

                template<class S>
                    void set(const S& value)
                    {
                        using std::fill;
                        fill(m_data,&m_data[D],value);
                    }
            protected:
                value_type      m_data[D];    ///< The actual Data
            };


        template<class T, int D>
            class vector;

        #define GENERATE_PARAM_ARG_ADDER(z,n,type)    T2 BOOST_PP_CAT(arg,BOOST_PP_ADD(n,1))
        #define GENERATE_PARAM_ARG2_ADDER(z,n,type)   this->m_data[BOOST_PP_ADD(n,1)] = static_cast<T>(BOOST_PP_CAT(arg,BOOST_PP_ADD(n,1)));

        #define CORE_GENERATE_OBJECT_FUNC(n)                                                                \
            template<class T>                                                                               \
                class vector<T,BOOST_PP_ADD(n,1)> : public basic_vector<T,BOOST_PP_ADD(n,1), vector<T,BOOST_PP_ADD(n,1)> >  \
                {                                                                                           \
                    typedef basic_vector<T,BOOST_PP_ADD(n,1),vector<T,BOOST_PP_ADD(n,1)> > base_type;       \
                    typedef vector<T,BOOST_PP_ADD(n,1)>                                     type;           \
                public:                                                                                     \
                    vector() {}                                                                             \
                    explicit vector(const T& value)                                                         \
                    :   base_type(value)                                                                    \
                    { }                                                                                     \
                                                                                                            \
                    template<class T2>                                                                      \
                        vector(T val0 BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n,GENERATE_PARAM_ARG_ADDER,val))   \
                        {                                                                                   \
                            this->m_data[0]   =   static_cast<T>(val0);                                     \
                            BOOST_PP_REPEAT(n,GENERATE_PARAM_ARG2_ADDER,val)                                \
                        }                                                                                   \
                                                                                                            \
																											\
					template<class S>																		\
						set_entry<type,1,BOOST_PP_ADD(n,1)> operator=(const S& value)						\
						{																					\
							this->m_data[0] = value;														\
						    return set_entry<type,1,BOOST_PP_ADD(n,1)>(*this);								\
						}																					\
                                                                                                            \
                };
/*
                    template<class T2>                                                                      \
                        inline type&    operator=(const vector<T2,BOOST_PP_ADD(n,1)>& v)                    \
                        {                                                                                   \
                            base_type::operator=(v);                                                        \
                            return *this;                                                                   \
                        }                                                                                   \
                                                                                                            \
                    using base_type::operator=;                                                             \
 */
        #define BOOST_PP_LOCAL_MACRO   CORE_GENERATE_OBJECT_FUNC
        #define BOOST_PP_LOCAL_LIMITS (1, CORE_MATH_LIMIT_VECTOR_DIMENSION)
        //DO NOT REMOVE WHITESPACE   ^
        #include BOOST_PP_LOCAL_ITERATE()

        #undef GENERATE_PARAM_ARG_ADDER
        #undef GENERATE_PARAM_ARG2_ADDER
        #undef GENERATE_OBJECT_FUNCS

        template<class T, int D>
            vector<T,D>  operator + (const vector<T,D>& v0, const vector<T,D>& v1)
            {
                vector<T,D> temp(v0);
                temp += v1;
                return temp;
            }

        template<class T, int D>
            vector<T,D> operator - (const vector<T,D>& v0, const vector<T,D>& v1)
            {
                vector<T,D> temp(v0);
                temp -= v1;
                return temp;
            }

        template<class T, int D, class S>
            vector<T,D>  operator + (const vector<T,D>& v, const S& s)
            {
                vector<T,D> temp(v);
                temp += s;
                return temp;
            }

        template<class T, int D, class S>
            vector<T,D> operator - (const vector<T,D>& v, const S& s)
            {
                vector<T,D> temp(v);
                temp -= s;
                return temp;
            }

        template<class T, int D, class S>
            vector<T,D>  operator + (const S& s, const vector<T,D>& v)
            {
                vector<T,D> temp(v);
                temp += s;
                return temp;
            }

        template<class T, int D, class S>
            vector<T,D> operator - (const S& s, const vector<T,D>& v)
            {
                vector<T,D> temp(s);
                temp -= v;
                return temp;
            }
        template<class T, int D, class S>
            vector<T,D>  operator * (const vector<T,D>& v, const S s)
            {
                vector<T,D> temp(v);
                temp*=s;
                return temp;
            }

        template<class T, int D, class S>
            vector<T,D>  operator / (const vector<T,D>& v, const S s)
            {
                vector<T,D> temp(v);
                temp/=s;
                return temp;
            }

        template<class T, int D>//, class S>
            vector<T,D>  operator * (const T s, const vector<T,D>& v)
            {
                vector<T,D> temp;
                temp*=s;
                return temp;
            }

        namespace aux {

            template<class T>
                struct divides_value
                {
                    divides_value(const T& t)
                    :   value(t)
                    { }

                    T operator()(const T& v0)
                    {
                        return value/v0;
                    }

                    T value;
                };
        }

        template<class T, int D, class S>
            vector<T,D>  operator / (const S s, const vector<T,D>& v)
            {
                vector<T,D> temp(v);
                transform(v,temp,aux::divides_value<T>(s));
                return temp;
            }
        //usefull shortcuts

        typedef vector<int,4> vector4i;
        typedef vector<int,3> vector3i;
        typedef vector<unsigned int,2> vector2ui;
        typedef vector<unsigned int,3> vector3ui;
        typedef vector<int,2> vector2i;

        typedef vector<float,4> vector4f;
        typedef vector<float,3> vector3f;
        typedef vector<float,2> vector2f;
        typedef vector<double,4> vector4d;
        typedef vector<double,3> vector3d;
        typedef vector<double,2> vector2d;

        template<class T>
            vector<T,3> cross(const vector<T,3>& v1,const vector<T,3>& v2)
            {
                return vector<T,3>(v1[1]*v2[2]-v1[2]*v2[1],v1[2]*v2[0]-v1[0]*v2[2],v1[0]*v2[1]-v1[1]*v2[0]);
            }

        template<class T>
            T cross(const vector<T,2>& v1,const vector<T,2>& v2)
            {
                return v1[0]*v2[1]-v1[0]*v2[1];
            }

        //scalar product shortcut
        template<class T, int D>
            T dot(const vector<T,D>& v0, const vector<T,D>& v1)
            {
                return scalar_product(v0,v1);
            }

        //norm_2 shortcut
        template<class T, int D>
            T norm(const vector<T,D>& v)
            {
                return norm_2(v);
            }

        template<class T, int D>
            T normsqr(const vector<T,D>& v)
            {
                return norm_2_sqr(v);
            }

        //relational operators for vector<T,n> for n from 2-GENERATE_MAX_PARAMS

        #define GENERATE_PARAM_OP_ADDER(z,n,OP)  && lhs[BOOST_PP_ADD(n,1)] OP rhs[BOOST_PP_ADD(n,1)]

        #define CORE_GENERATE_OBJECT_FUNCS(n)                                           \
                BOOST_PP_EXPR_IF(   BOOST_PP_LESS(n,CORE_MATH_LIMIT_VECTOR_DIMENSION),  \
                                    GENERATE_OBJECT_FUNC(n,==)                          \
                                    GENERATE_OBJECT_FUNC(n,<)                           \
                                    GENERATE_OBJECT_FUNC(n,>)                           \
                                    GENERATE_OBJECT_FUNC(n,<=)                          \
                                    GENERATE_OBJECT_FUNC(n,>=))

        #define GENERATE_OBJECT_FUNC(n,OP)                                                                              \
        template<class T>                                                                                               \
            inline bool operator OP (const vector<T,BOOST_PP_ADD(n,1)>& lhs, const vector<T,BOOST_PP_ADD(n,1)>& rhs)    \
            {                                                                                                           \
                return lhs[0] OP rhs[0] BOOST_PP_REPEAT(n,GENERATE_PARAM_OP_ADDER,OP);                                  \
            }                                                                                                           \

        template<class T,int  D>
            inline bool operator!=(const vector<T,D>& lhs, const vector<T,D>& rhs)
            {
                return !(lhs == rhs);
            }



        #define BOOST_PP_LOCAL_MACRO   CORE_GENERATE_OBJECT_FUNCS
        #define BOOST_PP_LOCAL_LIMITS (1, CORE_MATH_LIMIT_VECTOR_DIMENSION)
        //DO NOT REMOVE WHITESPACE   ^
        #include BOOST_PP_LOCAL_ITERATE()

        #undef GENERATE_PARAM_OP_ADDER
        #undef GENERATE_OBJECT_FUNC
        #undef CORE_GENERATE_OBJECT_FUNCS

        //serialization methods

        template<class CharT, class CharTraits, class T, int D>
            std::basic_ostream<CharT,CharTraits>& operator << (std::basic_ostream<CharT,CharTraits>& out, const vector<T,D>& v)
            {
                //FIXME: this only works with char

                out << '[' << v[0];

                for(typename vector<T,D>::const_iterator viter=v.begin()+1;viter!=v.end();++viter)
                    out << ',' << *viter;

                out << ']';
                //using std::copy;
                //copy(v.begin(),v.end(),std::ostream_iterator<T,CharT,CharTraits>(out,","));
                return out;
            }

        //TODO:for istream

        //overload math and vector traits
        template<int D, class T> struct is_vector<vector<T,D> > : public boost::mpl::true_ { };
        template<int D, class T> struct dimension<vector<T,D> > : public detail::static_dimension_impl<vector<T,D> ,D> { };
        template<int D, class T> struct field<vector<T,D> > { typedef T type; };

    } // namespace math

} // namespace core

#endif // _vector_hpp_
